import { MoveDirection } from "./enums"
import GameConfig from "./GameConfig"
import { Square } from "./Square"
import { SquareGroup } from "./SquareGroup"
import { createTeris } from "./Teris"
import { TerisRule } from "./TerisRule"
import { GameStatus, GameViewer } from "./types"

export class Game {
    // 游戏状态
    private _gameStatus: GameStatus = GameStatus.init
    // 当前操作的方块组
    private _curTeris?: SquareGroup
    // 下一个方块组
    private _nextTeris: SquareGroup
    // 下降定时器
    private _timer?: number
    // 下降难度，interval时间，默认1000ms
    private _duration: number = GameConfig.level[0].duration
    // 连续2行以上
    private _continuous: boolean = false
    // 积分
    private _score = 0

    get gameStatus() {
        return this._gameStatus
    }
    // 显示者
    constructor(private _viewer: GameViewer) {
        this._nextTeris = createTeris({ x: 0, y: 0 }) // 为了TS不报错
        this.createNext()
        this._viewer.init(this)
        this._viewer.showScore(this.score)
    }
    // 当前游戏中已存在的小方块
    private _exists: Square[] = []
    /**
     * 初始化操作
     */
    private init() {
        this._curTeris = undefined
        // 显示清空
        this._exists.forEach((sq) => {
            sq.viewer?.remove()
        })
        // 逻辑清空
        this._exists = []
        this.createNext()
        this.score = 0
    }
    /**
     * score的访问器，用于界面显示
     */
    get score() {
        return this._score
    }
    set score(val: number) {
        this._score = val
        this._viewer.showScore(this._score)
        // 分数和级别
        const level = GameConfig.level.filter((it) => it.score <= this._score).pop()!
        if (level.duration === this._duration) {
            return
        }
        if (this._timer) {
            clearInterval(this._timer)
            this._timer = undefined
            this._duration = level.duration
            this.autoDown()
        }
    }
    /**
     * 私有函数，生成初始的nextTeris，重新设置中心点，以及让显示者显示
     */
    private createNext() {
        this._nextTeris = createTeris({ x: 0, y: 0 })
        this.resetCenterPoint(this._nextTeris, GameConfig.nextSize.width)
        this._viewer.showNext(this._nextTeris)
    }
    /**
     * 开始
     */
    start() {
        // 如果已经开始：什么也不做
        if (this._gameStatus === GameStatus.playing) return
        // 如果游戏结束：需要初始化，重新来
        if (this._gameStatus === GameStatus.over) {
            this.init()
        }
        this._gameStatus = GameStatus.playing
        if (!this._curTeris) {
            this.switch()
        }
        this.autoDown()
        this._viewer.onGameStart()
    }
    /**
     * 内部方法：切换方块
     */
    private switch() {
        this._curTeris = this._nextTeris
        this.resetCenterPoint(this._curTeris, GameConfig.panelSize.width)
        // 当前方块出现时，就已经和之前方块重叠了
        const productRes = TerisRule.canIMove(this._curTeris.shape, this._curTeris.centerPoint, this._exists)
        if (!productRes) {
            // 游戏结束
            this._gameStatus = GameStatus.over
            clearInterval(this._timer)
            this._timer = undefined
            // 移除显示
            this._curTeris.squares.forEach((sq) => {
                sq.viewer?.remove()
            })
            this._viewer.onGameOver()
            return
        }
        this._viewer.switch(this._curTeris)
        this.createNext()
    }
    /**
     * 内部方法：自动下降
     */
    private autoDown() {
        if (this._timer && this._gameStatus === GameStatus.playing) {
            return
        }
        this._timer = window.setInterval(() => {
            if (this._curTeris) {
                if (!TerisRule.move(this._curTeris, MoveDirection.down, this._exists)) {
                    // 返回false则触底了，调用触底函数
                    this.hitBottom()
                }
            }
        }, this._duration)
    }
    /**
     * 暂停
     */
    pause() {
        if (this._gameStatus === GameStatus.playing) {
            this._gameStatus = GameStatus.pause
            window.clearInterval(this._timer)
            this._timer = undefined
            this._viewer.onGamePause()
        }
    }

    /**
     * 内部方法：判断生成的方块组坐标在容器里面是否居中上方
     */
    private resetCenterPoint(teris: SquareGroup, width: number) {
        const x = Math.floor(width / 2 - 1)
        const y = 0
        teris.centerPoint = { x, y }
        while (teris.squares.some((it) => it.point.y < 0)) {
            teris.centerPoint = {
                x: teris.centerPoint.x,
                y: teris.centerPoint.y + 1
            }
        }
    }
    /**
     * 左移
     */
    control_left() {
        if (this._curTeris && this._gameStatus === GameStatus.playing) {
            TerisRule.move(this._curTeris, MoveDirection.left, this._exists)
        }
    }
    /**
     * 右移
     */
    control_right() {
        if (this._curTeris && this._gameStatus === GameStatus.playing) {
            TerisRule.move(this._curTeris, MoveDirection.right, this._exists)
        }
    }
    /**
     * 下移
     */
    control_down() {
        if (this._curTeris && this._gameStatus === GameStatus.playing) {
            if (!TerisRule.moveDirectly(this._curTeris, this._exists)) {
                this.hitBottom()
            }
        }
    }
    /**
     * 旋转
     */
    control_rotate() {
        if (this._curTeris && this._gameStatus === GameStatus.playing) {
            TerisRule.rotate(this._curTeris, this._exists)
        }
    }
    /**
     * 触底触发函数
     */
    private hitBottom() {
        // 把方块组打散为小方块加入存在的数组中
        this._exists.push(...this._curTeris!.squares)
        // 处理移除
        const num = TerisRule.deleteSquare(this._exists)
        this.addScore(num)
        // 切换方块
        this.switch()
    }

    /**
     * 增加积分
     */
    private addScore(num: number): void {
        if (num === 0) {
            return
        } else if (num === 1) {
            this._continuous = false
            this.score = this.score + 1
        } else {
            if (num === 2) {
                if (this._continuous) {
                    this.score += 8
                } else {
                    this.score += 4
                }
            } else if (num === 3) {
                if (this._continuous) {
                    this.score += 20
                } else {
                    this.score += 9
                }
            } else {
                if (this._continuous) {
                    this.score += 50
                } else {
                    this.score += 20
                }
            }
            this._continuous = true
        }
    }
}
